package com.youan.log.modules.audit.dataimport;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.youan.log.common.threadtask.ThreadTaskProgressor;
import com.youan.log.modules.audit.dataimport.bean.DataFile;
import com.youan.log.modules.audit.dataimport.bean.TaskData;
import com.youan.log.modules.audit.entity.RegionData;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j(topic = "data.import")
public class DataImporter {

    private static final int COMMIT_LIST_SIZE = 10000;

    private TaskData taskData;
    private ThreadTaskProgressor taskProgress;

    private JdbcTemplate jdbcTemplate;
    private List<RegionData> dataList;

    private String sql;

    public DataImporter(TaskData taskData, ThreadTaskProgressor taskProgress) {
        this.taskData = taskData;
        this.taskProgress = taskProgress;

        this.sql = "INSERT INTO "+ taskData.getTableName() +" (id,direction,rule_id,source_ip,destination_ip,source_port," +
                "destination_port,date,time,payload,login_failure_account) values (?,?,?,?,?,?,?,?,?,?,?)";


        this.dataList = new ArrayList<>(COMMIT_LIST_SIZE);
        DataSource dataSource = SpringUtil.getBean(DataSource.class);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void importData() {
        importData(taskData.getSFile(), "s");
        importData(taskData.getDFile(), "d");
    }

    private void importData(DataFile dataFile, String type) {
        log.debug("开始提取数据");
        long start = 0;
        long end = 0;
        if(log.isDebugEnabled()) {
            start = System.currentTimeMillis();
        }

        // RandomAccessFile raf = new RandomAccessFile(dataFile.getPath(),"r")
        Path path = Paths.get(dataFile.getPath());
        try(BufferedReader bfr= Files.newBufferedReader(path)) {
            String s ;
            while((s =bfr.readLine())!=null){
                this.taskProgress.addProgressNum();
                try {
                    RegionData regionData = convert2Bean(s, type);
                    if(null == regionData) {
                        continue;
                    }

                    if(log.isDebugEnabled()) {
                        if(this.taskProgress.getProgressCurrentNum() % COMMIT_LIST_SIZE == 0) {
                            end = System.currentTimeMillis();
                            log.debug("读取文本{}行用时{}", COMMIT_LIST_SIZE, end - start);
                        }
                    }

                    addToList(regionData);

                    if(log.isDebugEnabled()) {
                        if (this.taskProgress.getProgressCurrentNum() % COMMIT_LIST_SIZE == 0) {
                            start = System.currentTimeMillis();
                        }
                    }


                }catch (Exception e) {
                    continue;
                }
            }
            pushToDB();
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    private void addToList(RegionData regionData) {
        this.dataList.add(regionData);
        if(this.dataList.size() >= COMMIT_LIST_SIZE) {
            log.debug("队列大小达到阈值,开始保存到数据库");
            pushToDB();
        }
    }

    private void pushToDB() {
        long start = 0;
        if(log.isDebugEnabled()) {
            start = System.currentTimeMillis();
        }
        this.jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                //id,direction,rule_id,source_ip,destination_ip,source_port,destination_port,date,time,payload,login_failure_account
                RegionData regionData = dataList.get(i);
                ps.setLong(1, IdWorker.getId());
                ps.setString(2, regionData.getDirection());
                ps.setString(3, regionData.getRuleId());
                ps.setString(4, regionData.getSourceIp());
                ps.setString(5, regionData.getDestinationIp());
                ps.setString(6, regionData.getSourcePort());
                ps.setString(7, regionData.getDestinationPort());
                ps.setString(8, regionData.getDate());
                ps.setString(9, regionData.getTime());
                ps.setString(10, regionData.getPayload());
                ps.setString(11, regionData.getLoginFailureAccount());
            }

            @Override
            public int getBatchSize() {
                return dataList.size();
            }
        });
        if(log.isDebugEnabled()) {
            long end = System.currentTimeMillis();
            log.debug("保存{}条数据到数据库，用时{}", this.dataList.size(), end-start);
        }
        this.dataList = new ArrayList<>(COMMIT_LIST_SIZE);
    }

    private RegionData convert2Bean(String line, String type) {
        line = line.trim();
        String[] dataArray = line.split("\\t");
        if(dataArray.length < 7) {
            return null;
        }

        RegionData regionData = new RegionData();
        regionData.setRuleId(dataArray[0]);

        Long sourceIp = Long.parseLong(dataArray[1]);
        regionData.setSourceIp(longToIP(sourceIp));

        Long destinationIp = Long.parseLong(dataArray[2]);
        regionData.setDestinationIp(longToIP(destinationIp));

        regionData.setSourcePort(dataArray[3]);
        regionData.setDestinationPort(dataArray[4]);

        String dateTime = dataArray[5];
        regionData.setDate(dateTime.split(" ")[0]);
        regionData.setTime(dateTime.split(" ")[1]);

        // 报错
        StringBuilder payload = new StringBuilder();
        for (int i = 6; i < dataArray.length; i++) {
            payload.append(dataArray[i]);
        }
        regionData.setPayload(payload.substring(8, payload.length()));
        regionData.setDirection(type);

        if(regionData.getRuleId().equals("10050001084")) {
            String loginFailureAccount = getLoginFailureAccount(regionData.getPayload());
            regionData.setLoginFailureAccount(loginFailureAccount);
        }


        return regionData;
    }

    private String getLoginFailureAccount(String payload) {
         String tmp = payload.substring(0, payload.indexOf("@")-1);
         String result = tmp.substring(tmp.indexOf("'")+1);
         return result;
    }

    /**
     * 将十进制整数形式转换成127.0.0.1形式的ip地址
     * 将整数形式的IP地址转化成字符串的方法如下：
     * 1、将整数值进行右移位操作（>>>），右移24位，右移时高位补0，得到的数字即为第一段IP。
     * 2、通过与操作符（&）将整数值的高8位设为0，再右移16位，得到的数字即为第二段IP。
     * 3、通过与操作符吧整数值的高16位设为0，再右移8位，得到的数字即为第三段IP。
     * 4、通过与操作符吧整数值的高24位设为0，得到的数字即为第四段IP。
     * @param longIp
     * @return
     */
    public static String longToIP(long longIp) {
        StringBuffer sb = new StringBuffer("");
        // 直接右移24位
        sb.append(String.valueOf((longIp >>> 24)));
        sb.append(".");
        // 将高8位置0，然后右移16位
        sb.append(String.valueOf((longIp & 0x00FFFFFF) >>> 16));
        sb.append(".");
        // 将高16位置0，然后右移8位
        sb.append(String.valueOf((longIp & 0x0000FFFF) >>> 8));
        sb.append(".");
        // 将高24位置0
        sb.append(String.valueOf((longIp & 0x000000FF)));
        return sb.toString();
    }

}
